/*
 *  Copyright 2008 Danny Lagrouw
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 */
package org.bastion.memory.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MemoryStore {

    private Map<Class<Object>, Map<Long, Object>> store = new HashMap<Class<Object>, Map<Long, Object>>();
    
    private Map<Class<Object>, Map<Object, Long>> revStore = new HashMap<Class<Object>, Map<Object,Long>>();

    private final Lock lock = new ReentrantLock();

    private Long lastId = 0L;

    public MemoryStore() {
    }
    
    public void clear() {
    	lastId = 0L;
    	store = new HashMap<Class<Object>, Map<Long, Object>>();
    	revStore = new HashMap<Class<Object>, Map<Object,Long>>();
    }
    
    protected Long getId() {
        lock.lock();
        try {
            lastId += 1;
            return lastId;
        } finally {
            lock.unlock();
        }
    }

    public Long register(Object object) {
        Long id = getId();
		getObjects(object.getClass()).put(id, object);
		getRevObjects(object.getClass()).put(object, id);
		return id;
    }

    private Map<Long, Object> getObjects(Class t) {
        Map<Long, Object> objects = store.get(t);
        if (objects == null) {
            objects = new HashMap<Long, Object>();
            store.put(t, objects);
        }
        return objects;
    }
    
    private Map<Object, Long> getRevObjects(Class t) {
    	Map<Object, Long> objects = revStore.get(t);
    	if (objects == null) {
    		objects = new HashMap<Object, Long>();
    		revStore.put(t, objects);
    	}
    	return objects;
    }
    
    public <T> T find(Class<T> t, Object id) {
        return (T) getObjects(t).get(id);
    }
    
    public void dump() {
    	System.out.print(this);
    }

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
        for (Class<Object> clazz : store.keySet()) {
            sb.append(clazz.getName() + "\n");
            Map<Long, Object> objects = store.get(clazz);
			for (Long id : objects.keySet()) {
                sb.append("- " + id + " -> " + objects.get(id) + "\n");
            }
        }
		return sb.toString();
	}

	public <T> List<T> executeQuery(Class<T> clazz) {
		return new ArrayList<T>((Collection<? extends T>) getObjects(clazz).values());
	}

	public Long generateId(Object object) {
		return getRevObjects(object.getClass()).get(object);
	}
    
}
